/**
 * @author     Gabor Greif <ggreif@gmail.com>
 * @copyright  2009 Gabor Greif
 * @license    http://www.opensource.org/licenses/mit-license.php  MIT License
 * @version    Release: 0.0
 */

#ifndef micker_COMMAND_hpp
#define micker_COMMAND_hpp

#include "Arities.hpp"
#include "Env.hpp"

#include <algorithm>
#include <deque>

namespace micker {
    
    typedef std::string (*CommandProc)(Env& env, const std::string& name, const std::deque<std::string>& args);

    struct Command : std::pair<CommandProc, Env*>
    {
        typedef std::pair<CommandProc, Env*> Inherited;
        Command(CommandProc proc, Env* env)
            : Inherited(proc, env)
            {}

        static std::string quote(const std::string&);

        template <unsigned ARITY, template <unsigned> class CRITERION = Exactly, typename TRAITS = ArityTraits<CRITERION<ARITY> > >
        struct ArgumentsMismatch
        {
            const bool good;
            ArgumentsMismatch(const std::deque<std::string>& args)
                : good(CRITERION<ARITY>::check(args.size()))
                {}
            operator bool(void) const
                {
                    return !good;
                }
            operator std::string(void) const
                {
                    return "[error {wrong number of arguments}]";
                }

            template <typename ENV, typename TRAITS::template With<ENV>::Worker PROC>
            static std::string proc(typename TRAITS::template With<ENV>::Environment env, const std::string& name, const std::deque<std::string>& args)
            {
                if (ArgumentsMismatch test = args)
                {
                    return test;
                }
                return TRAITS::template With<ENV>::template call<PROC>(env, name, args);
            }
        };
    };

    template <typename VAL>
    struct CommandTraits
    {
        template <typename T>
        struct PrefEnv : ValEnv<T> {};
    };

    template <typename VAL>
    struct CommandTraits<VAL&>
    {
        template <typename>
        struct PrefEnv : RefEnv<VAL> {};
    };

    template <>
    struct CommandTraits<void>
    {
        template <typename T>
        struct PrefEnv : NoEnv<T> {};
    };

#   define DEFINE_KNOWN_PRIMITIVE_TYPE(TYPE) \
        template <> \
        struct CommandTraits<TYPE> \
        { \
            template <typename T> \
            struct PrefEnv : PrEnv<T> {}; \
        }

    DEFINE_KNOWN_PRIMITIVE_TYPE(int);
    DEFINE_KNOWN_PRIMITIVE_TYPE(unsigned);

    template <typename VAL = void, template <typename> class WRAP = CommandTraits<VAL>::template PrefEnv>
    struct Comm : Command
    {
    protected:
        typedef typename WRAP<VAL>::simplify Env;
        typedef typename Env::preferred Pref;
        typedef std::string (*CommandProc)(Env& env, const std::string& name, const std::deque<std::string>& args);
    public:
        Comm(CommandProc proc, typename Env::from val = Env::defaulted())
            : Command(micker::CommandProc(proc), Env::create(val))
            {}
        Comm(CommandProc proc, Env env)
            : Command(micker::CommandProc(proc), Env::create(env))
            {}
    };

} // namespace

#endif // micker_COMMAND_hpp
